import numpy as np
from sklearn.preprocessing import MinMaxScaler, StandardScaler, RobustScaler, PowerTransformer
from scipy.stats import gaussian_kde
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.io as pio

# 1. 模拟数据（偏态 + 异常值）
np.random.seed(42)
x_normal = np.random.normal(50, 10, 1000)
x_tail = np.random.exponential(30, 300)
x_outliers = np.array([300, 400, 500])
x_raw = np.concatenate([x_normal, x_tail, x_outliers]).reshape(-1, 1)

# 2. 定义归一化方法
def transform_all(x):
    data = {}
    data['Raw'] = x
    data['MinMax'] = MinMaxScaler().fit_transform(x)
    data['Z-Score'] = StandardScaler().fit_transform(x)
    data['Log+Z-Score'] = StandardScaler().fit_transform(np.log1p(x))
    data['Robust'] = RobustScaler().fit_transform(x)

    # Box-Cox 要求正数
    x_pos = x[x > 0].reshape(-1, 1)
    boxcox = PowerTransformer(method='box-cox').fit_transform(x_pos)
    x_box_full = np.full_like(x, np.nan, dtype=np.float64)
    x_box_full[x > 0] = boxcox.flatten()
    data['Box-Cox'] = x_box_full
    return data

# 3. 应用所有变换
data_dict = transform_all(x_raw)
methods = list(data_dict.keys())
n_rows = len(methods)

# 4. 创建子图：每行两列（左：Histogram，右：KDE）
fig = make_subplots(
    rows=n_rows,
    cols=2,
    shared_yaxes=False,
    horizontal_spacing=0.15,
    subplot_titles=[
        f"{method} - Histogram" if j == 0 else f"{method} - KDE"
        for method in methods for j in range(2)
    ]
)

colors = {
    'Raw': 'gray',
    'MinMax': 'royalblue',
    'Z-Score': 'orange',
    'Log+Z-Score': 'green',
    'Robust': 'purple',
    'Box-Cox': 'red',
}

# 5. 绘图：每种方法两张图
for i, method in enumerate(methods):
    values = data_dict[method].flatten()
    values_clean = values[~np.isnan(values)]
    color = colors[method]

    # 直方图（左边）
    fig.add_trace(go.Histogram(
        x=values_clean,
        nbinsx=80,
        histnorm='density',
        marker_color=color,
        opacity=0.7,
        name=f"{method} Hist"
    ), row=i+1, col=1)

    # KDE 曲线（右边）
    kde = gaussian_kde(values_clean)
    x_range = np.linspace(values_clean.min(), values_clean.max(), 500)
    y_kde = kde(x_range)

    fig.add_trace(go.Scatter(
        x=x_range,
        y=y_kde,
        mode='lines',
        line=dict(color='black', width=2),
        name=f"{method} KDE"
    ), row=i+1, col=2)

# 6. 布局更新
fig.update_layout(
    height=300 * n_rows,
    width=1000,
    title_text="📊 不同归一化方法的直方图与密度分布对比（左右分图）",
    template="plotly_white",
    showlegend=False
)

# 7. 保存为 HTML
output_path = "normalized.html"
pio.write_html(fig, file=output_path, auto_open=True)
